#ifndef _MOE_MIGRATION
#define _MOE_MIGRATION

#include "engine/Population.hh"
#include "engine/Selection.hh"
#include "engine/netstream.hh"
#include <math.h>
#include <iostream>

using namespace std;
/**
* The migration operator it's implemented in this class, his porpouse it's transport indivuals
* from one island to another
*/

class Migration {

protected:

    float migrationRate;
    Selection *selection;

    NetStream *stream;

    /** array of pids from whom receive solutions*/
    int *inPids;
    int inLength;

    /** array of pids to send solutions*/
    int *outPids;
    int outLength;

    bool synchronized;

public:

    /**
    * Constructor
    *@param selection a selection operator instance is needed for select elements in the population in order to
    *migrate them
    *@param stream it's the netstream that bound the islands
    */
    Migration(Selection *selection, NetStream *stream) {
        migrationRate = 0.0;
        this->selection = selection;
        this->stream = stream;

        inPids = NULL;
        outPids = NULL;

        synchronized = false;
    }

    virtual ~Migration() {
        if (selection != NULL) delete selection;

        if (inPids != NULL) delete [] inPids;

        if (outPids != NULL) delete [] outPids;
    }

    void setMigrationRate(float migrationRate) {
        this->migrationRate = migrationRate;
    }

    float getMigrationRate() {
        return migrationRate;
    }

    void setSynchronized(bool synchronized) {
        this->synchronized = synchronized;
    }

    bool isSynchronized() {
        return synchronized;
    }

    /** this class uses the parameters migration.rate and migration.synchronized.
    * <p>migration.rate determines how many individuals will be involved in the migration,
    *    see the moea.properties for further details.</p>
    * <p>migration.synchronized if true synchronizes all islands at migration </p>
    */
/*    virtual void configure() {
        migrationRate = (float)atof(properties->get("migration.rate", "0.01"));

        if (strcmp("true", properties->get("migration.synchronized", "true")) == 0) synchronized = true;
        else synchronized = false;

    }*/

    virtual void send(Population *population) {
        int sendCount = (int)ceil(population->length() * migrationRate);


        ArrayList<Solution> *migrants = selection->select(population, sendCount);
        migrants->setOwner(true);

        for (int i = 0; i < outLength; i++) {
            (*stream) << set_target(outPids[i]);



            for (int k = 0; k < outLength; k++) {
                (*stream) << pack_begin;
                send(migrants->get(k));
                (*stream) << pack_end;
            }


        }

        delete migrants;
    }

    virtual void receive(Population *population) {
        ArrayList<Solution>* migrants = new ArrayList<Solution>();
        migrants->setOwner(true);
        int expectedSolutions = (int)ceil(population->length() * migrationRate);

        //dirty trick
        //pivot points to a instance of the sub-class of Solution, required for polymorphism
        //over receive method. See Migration::receive(Solution*)
        Solution *pivot = selection->select(population);


        for (int i = 0; i < inLength; i++) {
            (*stream) << set_source(inPids[i]);

            if (synchronized ) {
                (*stream) << wait(packed);

                while (migrants->length() < expectedSolutions) {
                    migrants->add(receive(pivot));
                }


            }
            else {

                int pending = false;
                //there is something in the wire?
                (*stream)._probe(packed, pending);

                while (migrants->length() < expectedSolutions && pending) {

                    migrants->add(receive(pivot));

                    (*stream)._probe(packed, pending);
                }

            }

        }


        inmigrationPolicy(population, migrants);
        delete pivot;
        delete migrants;
    }

    virtual void migrate(Population *population) {

        if (synchronized ) {
            //the synchronization point
            (*stream) << barrier;
        }

        send(population);

        receive(population);
    }

protected:

    virtual void send(Solution *solution) {
        (*stream) << pack_begin;
        solution->send((*stream));
        (*stream) << pack_end;
    }

    virtual Solution* receive(Solution* solution) {
        Solution *r = solution->copy();

        (*stream) << pack_begin;
        r->receive((*stream));
        (*stream) << pack_end;

        return r;
    }
    /**
    * this method initializes the outPids and inPids arrays. Creating in this way the topology of the islands
    */
    virtual void buildNet() = 0;

    /**
    * incorporates some, all or none of the set of inmigrants into the population according to the desired policy
    *@param population population that receive the inmigrants
    *@param inmigrants
    */
    virtual void inmigrationPolicy(Population *population, ArrayList<Solution>* inmigrants) = 0;
};

#endif
